package mbp.core;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.Random;

import mbp.base.EnumType;
import mbp.base.FeatureType;
import mbp.base.ServiceType;
import mbp.base.Settings;
import mbp.base.TimeUnit;
import mbp.exceptions.InvalidFeatureNameException;
import mbp.exceptions.InvalidFeatureTypeException;
import mbp.exceptions.InvalidFeatureValueException;

/**
 * Implementuje oferte. Oferte przesyla serwer uslug do klienta. Oferta jest rozszerzeniem
 * obiektu Service (Usluga) i zawiera dodatkowe informacje odnoszace sie do oferty.
 * @author Wojciech Wilanski
 *
 */
public class Offer extends Service
{
	//Identyfikator oferty
	private String uuid = null;
	
	//Data utworzenia oferty
	private Date offerCreateDate;
	
	//Data waznosci oferty - od utworzenia obiektu do popisania kontraktu
	private Date offerValidDate;
	
	//Lista na zasoby (informacje) jakie klient moze dostarczyc. Wykorzystywane przy kontrofercie
	//private List<resource> rcInAdvance;
	//private List<resource> rcInStuff;
	//private List<resource> rcInPay;
	
	
	//nazwa klienta i serwra ktorych dotyczy oferta
	private String clientName;
	private String serverName;
	
	private String agentName;
	
	//Referencja do obiektu Service na podstawie kt�rego zostala utworzona oferta
	//Jest to dodatkowa informacja (nie przesylana)
	//public Service serviceRef;
	
	private TimeUnit timeUnit = Settings.timeUnit;

	/**
	 * Utworzenie oferty z wartosciami domyslnymi uslugi. Obiekt wymaga inicjalizacji
	 * @param serviceType
	 */
	public Offer(ServiceType serviceType)
	{
		super(serviceType);
		
		//rcInAdvance = new ArrayList<resource>();
		//rcInStuff = new ArrayList<resource>();
		//rcInPay = new ArrayList<resource>();
		
		offerCreateDate = new Date();
	}
	
	/**
	 * Utoworzenie oferty na podstawie wczesniej zdefiniowanej uslugi. Wszystkie dane uslugi
	 * zostana skopiowane do pamieci wewnetrznej oferty.
	 * @param serviceType
	 * @param service
	 */
	public Offer(ServiceType serviceType, Service service)
	{
		super(serviceType);
		
		//rcInAdvance = new ArrayList<resource>();
		//rcInStuff = new ArrayList<resource>();
		//rcInPay = new ArrayList<resource>();
		
		offerCreateDate = new Date();
		initialize(service);
		initializeUUID();
	}
	
	/**
	 * Konstruktor wykorzystywany przy odtwarzaniu obiektu podczas komunikacji pomiedzy agentami.
	 * Pozwala ustawic identyfikator i date waznosci oferty.
	 * @param serviceType
	 * @param uuid
	 * @param validDate
	 */
	public Offer(ServiceType serviceType, String uuid, Date createDate)
	{
		super(serviceType);
		
		//rcInAdvance = new ArrayList<resource>();
		//rcInStuff = new ArrayList<resource>();
		//rcInPay = new ArrayList<resource>();
		
		this.offerCreateDate = createDate;
		this.uuid = uuid;
		calculateOfferValidDate();
	}
	
	public void initialize(String uuid, Date createDate)
	{
		this.uuid = uuid;
		this.offerCreateDate = createDate;
		calculateOfferValidDate();
	}
	
	//public void setUUID(String uuid)
	//{
	//	if(uuid == null)
	//}
	
	public void initialize(Service service)
	{
		//Pobranie referencji z obiektu service
		ArrayList<Resource> s_rc_out = service.getResourcesOut();
		ArrayList<ResourceValidation> s_rcv_in = service.getResourcesIn(-1);
		//ArrayList<Integer> rc_out_vs = service.rcOutValidSec;
		
		this.offerValidTU = new Integer(service.offerValidTU);
		
		this.realzeValidTU = new Integer(service.realzeValidTU);
		
		//Kopoiwanie wartosci zasobow wyjsciowych z obiektu service
		for(int i = 0; i<rcOut.size(); i++)
		{
			//Owner
			String owner = new String( s_rc_out.get(i).getOwner() );
			this.rcOut.get(i).setOwner(owner);
			
			//features
			Iterator<String> it = s_rc_out.get(i).getType().getFeaturesCollection().keySet().iterator();
			while(it.hasNext())
			{
				String key = it.next();
				try 
				{
					FeatureType type = this.rcOut.get(i).getFeatureType(key);
					switch(type)
					{
					case INT:
						Integer vint = new Integer((Integer) s_rc_out.get(i).getFeatureValue(key));
						this.rcOut.get(i).setFeatureValue(key, vint);
						//res.SetFeatureValue(key, 0);										
						break;
					case FLOAT:
						Float vfloat = new Float((Float) s_rc_out.get(i).getFeatureValue(key));
						this.rcOut.get(i).setFeatureValue(key, vfloat);
						break;
					case ENUM:
						EnumType venum = (EnumType) s_rc_out.get(i).getFeatureValue(key);
						//Object ob = new 
						//EnumType venumthis = (EnumType)this.rcOut.get(i).getFeatureValue(key);
						//venumthis.setValueInt(venum.toInt());
						this.rcOut.get(i).setFeatureValue(key, new String( venum.toString()));
						break;
					}
				} catch (InvalidFeatureNameException e) {
					// TODO Auto-generated catch block
					logger.error("InvalidFeatureNameException", e);
				} catch (InvalidFeatureTypeException e) {
					// TODO Auto-generated catch block
					logger.error("InvalidFeatureTypeException", e);
				} catch (InvalidFeatureValueException e) {
					// TODO Auto-generated catch block
					logger.error("InvalidFeatureValueException", e);
				}
			}
		}
		
		//Kopiowanie watrosci terminow dostarcaenia zasobow wejsciowych
		for(int i = 0; i<stuffValidTU.size(); i++)
		{
			stuffValidTU.set(i, new Integer(service.getResourcesInDeliveryTrems().get(i)));
		}
		
		//Kopiowanie wartosci zakresu wartosci cech dla zasobow wejsciowych z obiektu service
		for(int i = 0; i<rcvIn.size(); i++)
		{
			String cnd = new String( s_rcv_in.get(i).getCondition() );
			this.rcvIn.get(i).setCondition(cnd);
		}
		
		//Kopiowanie wartosci waznosci wyprodukowanych zasobow wyjsciowych
		for(int i =0; i<rcOutValidTU.size(); i++)
		{
			rcOutValidTU.set(i, new Integer(service.getResourcesOutValidTerms().get(i)));
		}
		
		calculateOfferValidDate();
	}
	
	/**
	 * Obliczenie identyfikatora oferty
	 */
	private void initializeUUID()
	{
		Date dt = new Date();
		Random random = new Random(); 
		BasicUUID basicUUID = new BasicUUID(0x01010101^dt.getTime(),dt.getTime(),random.nextInt());
		this.uuid = basicUUID.toString();
	}
	
	/**
	 * Zwraca mnoznik potrzebny do obliczania dat.
	 * @return
	 */
//	public int getMultiplier()
//	{
//		int multiplier = 1;
//		
//		//Ustawienie mnoznika dla jednostek czasu
//		switch(timeUnit)
//		{
//		case MINUTES:
//			multiplier = 60;
//			break;
//		case HOURS:
//			multiplier = 60 * 60;
//			break;
//		case DAYS:
//			multiplier = 60 * 60 * 24;
//			break;
//		}
//		
//		return multiplier;
//	}
	
	/**
	 * Oblicza date waznosci oferty
	 */
	private void calculateOfferValidDate()
	{
		//Wyznaczenie daty waznosci oferty (kontraktu)
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(offerCreateDate);
		calendar.add(Calendar.SECOND, getOfferValidTerm() * Settings.getMultiplier());
		offerValidDate = calendar.getTime();
	}
	
	
	/**
	 * Sprawdza czy oferta jest jeszcze wazna. Kontrakt jest oferta w okresie pomiedzy
	 * utworzeniem obiektu a podpisaniem kontraktu
	 * @return
	 */
	public boolean isOfferValid()
	{
		Date actualDate = new Date();
		return actualDate.before(offerValidDate);
	}
	
	/**
	 * Zwraca date waznosci oferty
	 * @return
	 */
	public Date getOfferValidityDate()
	{
		return new Date(this.offerValidDate.getTime());
	}
	
	/**
	 * Zwraca date utworzenia oferty
	 * @return
	 */
	public Date getOfferCreateDate()
	{
		return new Date(this.offerCreateDate.getTime());
	}
	
	@Override
	public void setOfferValidTerm(Integer value)
	{
		this.offerValidTU = value;
		calculateOfferValidDate();
	}
	
	public String getClientName() {
		return clientName;
	}

	public String getServerName() {
		return serverName;
	}
	
	public void setClientName(String clientName) {
		this.clientName = clientName;
	}
	
	public void setServerName(String serverName) {
		this.serverName = serverName;
	}
	
	
	public String GetUUID()
	{
		return this.uuid;
	}

	public String getAgentName() {
		return agentName;
	}

	public void setAgentName(String agentName) {
		this.agentName = agentName;
	}

}
